package com.cooper.scraper;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

import com.cooper.android.comm.BitmapUtils;

public class HttpDownload {

	private static final String POST_URL_REG = "(.+)\\s#POST\\[(.+)\\]$";
	private static final String POST_PARAM_SPLIT_REG = "\"(.*?)\":\"(.*?)\"(,|$)";

	public HttpURLConnection createDefaultConn(String url)
			throws MalformedURLException, IOException {
		return createDefaultConn(url, null);
	}

	public Map<String, String> createDefaultHeadersProperty(String url) {
		Map<String, String> result = new HashMap<String, String>();
		result.put(
				"Accept",
				"image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
		result.put("Referer", url);
		result.put(
				"User-Agent",
				"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
		result.put("Connection", "Keep-Alive");
		return result;
	}

	public HttpURLConnection createDefaultConn(String url,
			Map<String, String> headers) throws MalformedURLException,
			IOException {
		HttpURLConnection result = (HttpURLConnection) new URL(url)
				.openConnection();
		result.setConnectTimeout(params.getConnTimeout());
		result.setRequestMethod("GET");

		if (headers == null) {
			headers = createDefaultHeadersProperty(url);
		}
		for (Entry<String, String> entry : headers.entrySet())
			result.setRequestProperty(entry.getKey(), entry.getValue());

		return result;
	}

	protected DownloadParams params;

	public HttpDownload(DownloadParams params) {
		this.params = params;
	}

	public DownloadParams getParams() {
		return params;
	}

	public String getRedirectUrl(String oldUrl) {
		return getRedirectUrl(oldUrl, null);
	}

	public String getRedirectUrl(String oldUrl, Map<String, String> headers) {
		try {
			HttpURLConnection conn = createDefaultConn(oldUrl, headers);
			try {
				conn.connect();
				if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
					return conn.getURL().toString();
				} else if (conn.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP) {
					return getRedirectUrl(conn.getHeaderField("Location"),
							headers);
				} else {
					Log.d("HttpDownload",
							"Connect error:" + conn.getResponseCode());
					throw new IOException("ConnectError"
							+ conn.getResponseCode());
				}
			} finally {
				conn.disconnect();
			}
		} catch (Exception e) {
			return oldUrl;
		}
	}

	public boolean urlExists(String url, Map<String, String> headers)
			throws MalformedURLException, IOException, DownloadException {
		HttpURLConnection conn = createDefaultConn(url, headers);
		try {
			// conn.setConnectTimeout(params.getConnTimeout());
			conn.connect();
			switch (conn.getResponseCode()) {
			case HttpURLConnection.HTTP_CLIENT_TIMEOUT:
			case HttpURLConnection.HTTP_GATEWAY_TIMEOUT:
				throw new DownloadException("timeout");
			case HttpURLConnection.HTTP_ACCEPTED:
			case HttpURLConnection.HTTP_OK:
				return true;
			default:
				return false;
			}
		} finally {
			conn.disconnect();
		}
	}

	public boolean urlExists(String url) throws MalformedURLException,
			IOException, DownloadException {
		return urlExists(url, new HashMap<String, String>(0));
	}

	public boolean urlCanUse(String url, Map<String, String> headers) {
		try {
			return urlExists(url, headers);
		} catch (Exception e2) {
			return false;
		}
	}

	public boolean urlCanUse(String url) {
		try {
			return urlExists(url);
		} catch (Exception e2) {
			return false;
		}
	}

	private String downloadPage(String url, String encoding, int retry,
			boolean useCache, Map<String, String> headers)
			throws DownloadException {
		try {
			if (useCache && params.getPageBuffer() != null) {
				String buff = params.getPageBuffer().get(url);
				if (buff != null) {
					Log.d("Downloader", "get Page from Buffer:" + url);
					return buff;
				}
			}

			String result;
			Pattern p = Pattern.compile(POST_URL_REG);
			Matcher m = p.matcher(url);
			if (m.find()) {
				String postUrl = m.group(1);
				String postParamStr = m.group(2);
				p = Pattern.compile(POST_PARAM_SPLIT_REG);
				m = p.matcher(postParamStr);
				List<NameValuePair> postParams = new ArrayList<NameValuePair>();
				while (m.find()) {
					postParams.add(new BasicNameValuePair(m.group(1), m
							.group(2)));
				}
				result = httpPostPage(postUrl, postParams, encoding);
			} else {
				result = httpGetPage(url, encoding, headers);
			}

			if ((result == null || "".equals(result.trim()))
					&& !params.isCancelled()) {
				Log.d("HttpDownload", "result:" + result + "|url:" + url
						+ "| encoding:" + encoding);
				throw new DownloadException(url);
			}

			if (useCache && result != null && params.getPageBuffer() != null
					&& !result.trim().equals(""))
				params.getPageBuffer().put(url, result.toString());
			return result;
		} catch (Exception e) {
			if (!params.isCancelled()) {
				if (retry <= params.getRetryCount()) {
					Log.e("Downpage", "retry:" + e.getMessage(), e);
					return downloadPage(url, encoding, ++retry, useCache,
							headers);
				} else
					throw new DownloadException(e);
			} else
				return null;
		}
	}

	public String downloadPage(String url, String encoding)
			throws DownloadException {
		return downloadPage(url, encoding, 0, true, null);
	}

	public String downloadPage(String url) throws DownloadException {
		return downloadPage(url, null);
	}

	public String downloadPage(String url, String encoding, boolean useCache)
			throws DownloadException {
		return downloadPage(url, encoding, 0, useCache, null);
	}

	public String downloadPage(String url, boolean useCache)
			throws DownloadException {
		return downloadPage(url, null, 0, useCache, null);
	}

	public String downloadPage(String url, String encoding, boolean useCache,
			Map<String, String> headers) throws DownloadException {
		return downloadPage(url, encoding, 0, useCache, headers);
	}

	private String httpPostPage(String url, List<NameValuePair> nvparams,
			String encoding) throws ClientProtocolException, IOException {
		DefaultHttpClient httpClient = new DefaultHttpClient(
				params.createHttpParameters());
		try {
			HttpPost httpRequest = new HttpPost(url);
			httpRequest.setEntity(new UrlEncodedFormEntity(nvparams, encoding));
			HttpResponse httpResponse = httpClient.execute(httpRequest);
			if ((httpResponse.getStatusLine().getStatusCode() == 200)
					&& !params.isCancelled()) {
				return EntityUtils.toString(httpResponse.getEntity());
			} else {
				return null;
			}
		} finally {
			httpClient.getConnectionManager().shutdown();
		}
	}

	private String httpGetPage(String url, String encoding,
			Map<String, String> headers) throws ClientProtocolException,
			IOException {
		Log.d("Downloader", "httpGet page:" + url);
		DefaultHttpClient httpClient = new DefaultHttpClient(
				params.createHttpParameters());
		try {
			HttpGet request = new HttpGet(url);

			if (headers == null) {
				headers = createDefaultHeadersProperty(url);

			}
			for (Entry<String, String> entry : headers.entrySet()) {
				request.setHeader(entry.getKey(), entry.getValue());
			}

			HttpResponse response = httpClient.execute(request);
			HttpEntity entity = response.getEntity();

			boolean gzipEncoding = false;
			Header[] encodingHeaders = response.getHeaders("Content-Encoding");
			for (Header encodingHeader : encodingHeaders) {
				if ("gzip".equals(encodingHeader.getValue())) {
					gzipEncoding = true;
				}
			}
			InputStream is = entity.getContent();
			if (gzipEncoding) {
				is = new GZIPInputStream(is);
			}
			if (is != null) {
				try {
					InputStreamReader inputStreamReader;
					if (encoding == null || "".equals(encoding)) {
						inputStreamReader = new InputStreamReader(is);
					} else {
						inputStreamReader = new InputStreamReader(is, encoding);
					}
					BufferedReader in = new BufferedReader(inputStreamReader,
							10 * 1024);
					try {
						String str = "";
						StringBuffer result = new StringBuffer("");
						while ((str = in.readLine()) != null
								&& !params.isCancelled()) {
							result.append(str);
						}
						if (!params.isCancelled()) {
							return result.toString();
						} else
							return null;
					} finally {
						in.close();
					}
				} finally {
					is.close();
				}
			} else
				return null;
		} finally {
			httpClient.getConnectionManager().shutdown();
		}
	}

	public Bitmap downloadBitmap(String url, Map<String, String> headers)
			throws DownloadException {
		return downloadBitmap(url, headers, 0);
	}

	public Bitmap downloadBitmap(String url) throws DownloadException {
		return downloadBitmap(url,"");
	}

	public Bitmap downloadBitmap(String url, String referer)
			throws DownloadException {
		Map<String, String> headers = null;
		if (referer != null && "".equals(referer.trim())) {
			headers = createDefaultHeadersProperty(url);
			headers.put("Referer", referer);
		}
		return downloadBitmap(url, headers, 0);
	}

	private Bitmap downloadBitmap(String url, Map<String, String> headers,
			int retry) throws DownloadException {
		try {

			Bitmap result = httpGetBitmap(url, headers);
			if (result == null && !params.isCancelled()) {
				throw new DownloadException(url);
			}
			return result;
		} catch (Exception e) {
			if (!params.isCancelled()) {
				if (retry <= params.getRetryCount()) {
					return downloadBitmap(url, headers, ++retry);
				} else
					throw new DownloadException(e);
			} else
				return null;
		}
	}

	private Bitmap httpGetBitmap(String url, Map<String, String> headers)
			throws ClientProtocolException, IOException {
		Bitmap buff = params.getBitmapFromCache(url);
		if (buff != null) {
			Log.d("HttpDownload", "GET Bitmap from Cache");
			return buff;
		}

		DefaultHttpClient httpClient = new DefaultHttpClient(
				params.createHttpParameters());
		try {
			HttpGet request = new HttpGet(url);
			if (headers == null) {
				headers = createDefaultHeadersProperty(url);

			}
			for (Entry<String, String> entry : headers.entrySet()) {
				request.setHeader(entry.getKey(), entry.getValue());
			}
			HttpResponse response = httpClient.execute(request);
			HttpEntity entity = response.getEntity();

			BufferedHttpEntity bufferedHttpEntity = new BufferedHttpEntity(
					entity);
			InputStream is = bufferedHttpEntity.getContent();
			if (is != null) {
				try {
					Bitmap result = BitmapFactory
							.decodeStream(new BitmapUtils.FlushedInputStream(is));
					if (result != null && !params.isCancelled())
						params.cacheBitmap(url, result);
					return result;
				} finally {
					is.close();
				}
			} else
				return null;
		} finally {
			httpClient.getConnectionManager().shutdown();
		}
	}

	public void downloadFile(String url, File file) throws DownloadException {
		downloadFile(url, null, file);
	}

	public void downloadFile(String url, String referer, File file)
			throws DownloadException {
		Map<String, String> headers = null;
		if (referer != null && "".equals(referer.trim())) {
			headers = createDefaultHeadersProperty(url);
			headers.put("Referer", referer);
		}
		downloadFile(url, headers, file, 0);
	}

	public void downloadFile(String url, File file, Map<String, String> headers)
			throws DownloadException {
		downloadFile(url, headers, file, 0);
	}

	private void downloadFile(String url, Map<String, String> headers,
			File file, int retry) throws DownloadException {
		try {
			httpGetFile(url, file, headers);
		} catch (Exception e) {
			if (retry <= params.getRetryCount()) {
				downloadFile(url, headers, file, ++retry);
			} else {
				throw new DownloadException(e);
			}
		}
	}

	private void httpGetFile(String url, File file, Map<String, String> headers)
			throws ClientProtocolException, IOException {
		DefaultHttpClient client = new DefaultHttpClient(
				params.createHttpParameters());
		try {
			HttpGet request = new HttpGet(url);

			if (headers == null) {
				headers = createDefaultHeadersProperty(url);

			}
			for (Entry<String, String> entry : headers.entrySet()) {
				request.setHeader(entry.getKey(), entry.getValue());
			}

			HttpResponse response = client.execute(request);
			HttpEntity entity = response.getEntity();
			// long length = entity.getContentLength();
			InputStream is = entity.getContent();
			if (is != null) {
				if (!file.getParentFile().exists()){
					file.getParentFile().mkdirs();
				}
				
				FileOutputStream fileOutputStream = new FileOutputStream(file);
				try {
					byte[] buf = new byte[1024];
					int ch = -1;
					while ((ch = is.read(buf)) != -1) {
						if (!params.isCancelled()) {
							fileOutputStream.write(buf, 0, ch);
						} else if (file.exists()) {
							file.delete();
						}
					}
					if (!params.isCancelled())
						fileOutputStream.flush();
				} finally {
					is.close();
					fileOutputStream.close();
				}
			}
		} finally {
			client.getConnectionManager().shutdown();
		}
	}
	
	public String getRemoteFileExtName(String url){
		try {
			HttpURLConnection conn = createDefaultConn(url);
			if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
				return getRemoteFileExtName(conn,url);
			}else {
				return getFileExtNameForUrl(url);
			}
		} catch (MalformedURLException e) {
			Log.e("HttpDownload",e.getMessage(),e);
		} catch (IOException e) {
			Log.e("HttpDownload",e.getMessage(),e);
		}
		return "";
	}

	private String getFileExtNameForUrl(String url){
		String filename = url.substring(url.lastIndexOf('/') + 1);
		if (filename != null && !"".equals(filename.trim())) {
			if (filename.indexOf('?') > 0) {
				filename = filename.substring(0, filename.indexOf('?'));
			}
			if (filename.indexOf('.') > 0){
				return filename.substring(filename.lastIndexOf('.') + 1);
			}
		}
		return "";
	}
	
	public String getRemoteFileExtName(HttpURLConnection conn, String url) {
		String filename = url.substring(url.lastIndexOf('/') + 1);
		if (filename == null || "".equals(filename.trim())) {
			for (int i = 0;; i++) {
				String mine = conn.getHeaderField(i);
				if (mine == null)
					break;
				if ("content-disposition".equals(conn.getHeaderFieldKey(i)
						.toLowerCase())) {
					Matcher m = Pattern.compile(".*filename=(.*)").matcher(
							mine.toLowerCase());
					if (m.find())
						filename = m.group(1);
				}
			}
		}
		if (filename != null && !"".equals(filename.trim())) {
			String result = filename.substring(filename.lastIndexOf('.') + 1);

			if (result != null && !"".equals(result.trim())) {

				if (result.indexOf('?') > 0) {
					result = result.substring(0, result.indexOf('?'));
				}
				return result;
			}
		}
		return "";
	}

}
